from tortoise.transactions import in_transaction
from datetime import date,timedelta
import time
from app.tactics.models.found_lot import FoundLot
from app.tactics.models.found_lot_tatics_stage import FoundLotTacticsStage
from app.tactics.models.found_lot_trade_flow import FoundLotTradeFlow
from app.tactics.models.found_everyday_siprice import FoundEverydaySiPrice
from app.tactics.models.found_lot_trade_match import FoundLotTradeMatch
from app.tactics.models.found_lot_everyday_balance import FoundLotEverydayBalance
from app.tactics.routes import update_balance

import logging
logger = logging.getLogger(__name__)
import traceback



class FundTradingStrategy:
    def __init__(self):
        pass

    async def sell_lots_based_on_profit(self,lot,calc_date):
        """
        查找未核销的买入批次，如果其盈利金额和收益率达标，则进行卖出操作。
        卖出时需要检查手续费是否符合要求，并且保证卖出后持仓不低于基准份额。
        卖出批次与买入批次绑定，标记为已核销。
        """
        """每天早上运行任务---卖出只能基于昨天的价格来做决策"""
        async with in_transaction() as connection:
            try:
                yesterday_date = calc_date + timedelta(days=-1)
                #查询昨日余额是否获取到
                si_price = await FoundEverydaySiPrice.filter(record_status=1,
                                                             found_code= lot.found_code,base_date=yesterday_date).first()
                if not si_price:
                    return True
                # 查找未核销的买入批次
                buy_flows = await FoundLotTradeFlow.filter(record_status=1,lot_code= lot.lot_code,trade_type='in',
                                                           trade_shares__gt=0,
                                                          trade_date__lt = calc_date,match_id=0).all()
                buy_buy_flows_sorted = sorted(buy_flows, key=lambda x: x.trade_date)


                for buy_flow in buy_buy_flows_sorted:
                    # 计算盈利金额和收益率
                    profit_amount = (si_price.base_si_price - buy_flow.trade_si_price)*buy_flow.trade_shares
                    profit_rate = profit_amount / buy_flow.trade_amount
                    found_stage = await FoundLotTacticsStage.filter(record_status=1,lot_code= lot.lot_code,min_price__lt=si_price.base_si_price,max_price__gte= si_price.base_si_price ).first()
                    if not found_stage:
                        return True
                    if profit_rate>0.06:
                        print('==buy_flow_id=%s====calc_date==%s===profit_amount=====%s===profit_rate==%s=' % (buy_flow.id,calc_date,round(profit_amount,2), 100*round(profit_rate,4)  ))
                    # 判断是否达标
                    if profit_amount >= found_stage.out_earn_amount and 100*profit_rate >= found_stage.out_earn_rate:
                        # 判断手续费
                        if await self.is_fee_valid(buy_flow):
                            # 核销处理并卖出
                            print('=!!!!!!!!!!!!=buy_flow_id=%s====calc_date==%s===profit_amount=====%s===profit_rate==%s=' % (
                            buy_flow.id, calc_date, round(profit_amount, 2), 100 * round(profit_rate, 2)))

                            msg="盈利金额[%s]和收益率[%s]达标,自动卖出;"%(round(profit_amount,2),100*round(profit_rate,2))
                            sell_flow= await self.process_sell(buy_flow,calc_date, msg,connection)
                            trade_math =await self.bind_lots(buy_flow, sell_flow,connection)
                            buy_flow.match_id = trade_math.id
                            await buy_flow.save()
                            sell_flow.match_id= trade_math.id
                            await sell_flow.save()

                            print(f"SellID {sell_flow.id}   bound  BuyID {buy_flow.id}")
            except Exception as e:
                # 出现异常时，事务会自动回滚
                print(f"Error: {e}")
                raise

    async def sell_based_on_cumulative_profit(self,lot,cur_date):
        """
        如果累计收益金额达到要求，则对最早的批次进行卖出操作。
        卖出时需要检查手续费是否符合要求，并将卖出批次与买入批次绑定。
        """
        async with in_transaction() as connection:
            try:
                flag = await self.is_cumulative_profit_sufficient(lot,cur_date)
                if flag:
                    # 查找累计收益金额达到要求的买入批次
                    buy_flows = await FoundLotTradeFlow.filter(record_status=1, lot_code=lot.lot_code, trade_type='in',
                                                               trade_shares__gt=0,
                                                               trade_date__lt=cur_date, match_id=0).all()
                    buy_flows = sorted(buy_flows, key=lambda x: x.trade_date) # 按交易时间排序
                    buy_flow =buy_flows and buy_flows[0] or False
                    if buy_flow and await self.is_fee_valid(buy_flow):
                        msg = "总收益达标卖出"
                        sell_flow = await self.process_sell(buy_flow, cur_date, msg, connection)
                        trade_math = await self.bind_lots(buy_flow, sell_flow, connection)
                        buy_flow.match_id = trade_math.id
                        await buy_flow.save()
                        sell_flow.match_id = trade_math.id
                        await sell_flow.save()
            except Exception as e:
                # 出现异常时，事务会自动回滚
                print(f"Error: {e}")
                raise

    async def buy_more_if_needed(self,lot,cur_date):
        """
        当日无卖出批次且投入的成本金额低于楼层的投入上限时，进行一次加仓买入操作。
        """
        async with in_transaction() as connection:
            try:
                # 判断当日是否有卖出批次
                has_sell_lot_today = await FoundLotTradeFlow.filter(trade_date=cur_date, trade_type='out',record_status=1).exists()

                if not has_sell_lot_today:
                    yesterday_date = cur_date + timedelta(days=-1)
                    #已投入总金额
                    balance = await FoundLotEverydayBalance.filter(base_date=yesterday_date,record_status=1,lot_code=lot.lot_code).first()
                    if not balance:
                        return True
                    # 查找当前楼层
                    current_stage = await self.get_current_stage(lot,cur_date)
                    if not current_stage:
                        print('===%s===%s==没有找到stage==='%(lot.lot_code,cur_date  ))
                        return True
                    if current_stage.in_base_amount > balance.holding_amount:
                        msg="stageID:%s 持仓%s 不足%s"%(current_stage.id,balance.holding_amount,current_stage.in_base_amount)
                        await self.buy_lot(cur_date,current_stage, lot,msg,connection)
                        print(f"Bought more for stage {current_stage.seq}.")
            except Exception as e:
                # 出现异常时，事务会自动回滚
                print(f"Error: {e}")
                raise

    async def is_fee_valid(self, flow):
        """
        验证手续费是否符合要求。
        实现逻辑可能涉及计算手续费并与要求进行比较。
        """
        # 示例：手续费验证逻辑
        return True  # 示例返回值

    async def process_sell(self, buy_flow, calc_date,msg,connection):
        """
        处理卖出操作，包括更新买入批次的状态为已卖出。
        """
        #lot.record_status = 0  # 标记为已卖出
        sell_flow_dict={
            'lot_code':buy_flow.lot_code,
            'found_code': buy_flow.found_code,
            'found_name': buy_flow.found_name,
            'trade_type': 'out',
            'trade_date': calc_date,
            'trade_amount': 0,
            'trade_shares': -1*buy_flow.trade_shares,
            'trade_si_price':0,
            'trade_remark':msg}
        sell_flow = await FoundLotTradeFlow.create(**sell_flow_dict)
        await sell_flow.save(using_db=connection)
        return sell_flow


    async def bind_lots(self, buy_flow,sell_flow, connection):
        """
        将卖出批次与买入批次绑定。
        可能涉及创建新的记录表示绑定关系。
        """
        # 实现买入批次与卖出批次的绑定逻辑
        match_dict={
            "lot_code":buy_flow.lot_code,
            "found_code": buy_flow.found_code,
            "found_name": buy_flow.found_name,
            "match_type": "自动",
            "match_shares": buy_flow.trade_shares,
            "buy_trade_flow_id": buy_flow.id,
            "buy_price": buy_flow.trade_si_price,
            "buy_amount": buy_flow.trade_amount,
            "buy_date": buy_flow.trade_date,
            "sell_trade_flow_id": sell_flow.id,
            "sell_price": sell_flow.trade_si_price,
            "sell_amount": sell_flow.trade_amount,
            "sell_date": sell_flow.trade_date,

        }
        trade_math = await FoundLotTradeMatch.create(**match_dict)
        await trade_math.save(using_db=connection)
        return trade_math

    async def is_cumulative_profit_sufficient(self, lot,cur_date):
        """
        验证累计收益金额是否达到要求。
        """
        # 示例：累计收益金额验证逻辑
        yesterday_date = cur_date + timedelta(days=-1)
        #卖出是基于昨天的价格做决策的
        balance = await FoundLotEverydayBalance.filter(base_date=yesterday_date, record_status=1,
                                                       lot_code=lot.lot_code).first()
        if not balance:
            return False
        if balance.total_earn_amount >= lot.fore_earn_amount:
            return True  # 示例返回值
        else:
            return False

    async def get_current_stage(self,lot,cur_date):
        """
        获取当前楼层。
        可能涉及从数据库中查询当前的策略阶段信息。
        """
        si_price = await FoundEverydaySiPrice.filter(record_status=1, found_code=lot.found_code,
                                                     base_date=cur_date).first()
        if not si_price:
            return False
        stage = await FoundLotTacticsStage.filter(record_status=1,lot_code = lot.lot_code,
                                    min_price__lt=si_price.base_si_price,max_price__gte=si_price.base_si_price).first()

        return stage

    async def buy_lot(self, cur_date,stage,lot, msg,connection):
        """
        进行加仓买入操作。
        可能涉及创建新的基金批次记录。
        """
        # 实现加仓买入的逻辑
        sell_flow_dict={
            'lot_code':lot.lot_code,
            'found_code': lot.found_code,
            'found_name': lot.found_name,
            'trade_type': 'in',
            'trade_date': cur_date,
            'trade_amount':stage.in_onetime_buy_amount,
            'trade_shares': 0,
            'trade_si_price':0,
            'trade_remark':msg
        }
        sell_flow = await FoundLotTradeFlow.create(**sell_flow_dict)
        await sell_flow.save(using_db=connection)
        return sell_flow


    async def schedule_tasks(self):
        """
        调度任务，根据策略执行买入和卖出操作。
        """
        logger.info(f"自动策略执行开始 executed at {time.strftime('%Y-%m-%d %H:%M:%S')}")
        tday =  date.today()
        try:
            #查找未更新的基金批次
            found_lots = await FoundLot.filter(record_status=1,lot_update_date__lt= tday ).all()
            for lot in found_lots:
                start_date = lot.lot_update_date
                end_date = tday

                while(start_date<end_date):
                    cur_date =start_date + timedelta(days=1)
                    # 执行卖出策略
                    await self.sell_lots_based_on_profit(lot,cur_date)
                    await self.sell_based_on_cumulative_profit(lot,cur_date)
                    # 执行买入策略
                    await self.buy_more_if_needed(lot,cur_date)
                    #更新余额
                    await update_balance(lot.lot_code)
                    start_date = cur_date
                lot.lot_update_date = cur_date
                await lot.save()
            logger.info(f"自动策略 执行完成 {time.strftime('%Y-%m-%d %H:%M:%S')}")

        except Exception as e:
            # 捕获异常，输出错误信息
            logger.error(traceback.format_exc())
            print(f"调度任务发生错误: {e}")

